#!/usr/bin/env ruby 
# == SETUP
#   KEYS: http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key
#   put a config file (s3sync.yml) in the same directory as this file
#
# == Config Example 
#   aws_access_key: 11111111111111111111111
#   aws_secret_access_key: 222222222222222222222
#   bucket_name: testbucket
#   files: 
#     public/: static #upload all public files ito the static folder
#
# == Examples
#   Create Your Bucket on S3
#     ruby s3sync.rb create
#
#   Upload all Files/Changes to S3
#     ruby s3sync.rb commit
#
#   Download all Files/Changes to S3
#     ruby s3sync.rb update
#
# == Usage 
#   ruby s3sync.rb [options] command
#
#   For help use: ruby s3sync.rb -h
#
# == Options
#   -h, --help          Displays help message
#   -v, --version       Display the version, then exit
#   -q, --quiet         Output as little as possible, overrides verbose
#   -V, --verbose       Verbose output
#   TO DO - add additional options
#
# == Author
#   Michael Grosser
#
# == Bugs/Suggestions 
#   code.google.com/p/simples3sync
#
# == Copyright
#   Copyright (c) 2007 Michael Grosser. Licensed under the MIT License:
#   http://www.opensource.org/licenses/mit-license.php

require 'optparse' 
require 'rdoc/usage'
require 'ostruct'

class SimpleS3SyncConfig
  VERSION = '0.0.1'

  attr_reader :options

  def initialize(arguments)
    @arguments = arguments

    # Set defaults
    @options = OpenStruct.new
    @options.verbose = false
    @options.quiet = false
    @options.config = 's3sync.yml'
  end

  # Parse options, check arguments, then process the command
  def run
    if parsed_options? && arguments_valid? 
      puts "Start at #{DateTime.now}\n\n" if @options.verbose
      process_command
      puts "\nFinished at #{DateTime.now}" if @options.verbose
    else
      output_usage!
    end
  end

  protected

    def parsed_options?
      # Specify options to read
      opts = OptionParser.new 
      opts.on('-v', '--version')    { output_version ; exit 0 }
      opts.on('-h', '--help')       { output_help! }
      opts.on('-V', '--verbose')    { @options.verbose = true }  
      opts.on('-q', '--quiet')      { @options.quiet = true }
      opts.on('-c', '--config X') do |config|
        @options.config = File.dirname(__FILE__)+'/'+config
      end
      
      opts.parse!(@arguments) rescue return false
      process_options
    end

    # Performs post-parse processing on options
    def process_options
      @options.verbose = false if @options.quiet
      parse_config
    end

    def output_options
      puts "Options:\n"

      @options.marshal_dump.each do |name, val|        
        puts "  #{name} = #{val}"
      end
    end

    # exits when no keys where found
    def arguments_valid?
      if !@options.aws_access_key || !@options.aws_secret_access_key 
        puts 'No keys where found aws_access_key + aws_secret_access_key required!'
        RDoc::usage 'Config Example' #exits app
      end
    end
   
    def parse_config
      #file exists, path is not absolute       
      if !File.exists? @options.config 
        puts "Config file not found: #{@options.config}" 
        RDoc::usage 'SETUP' #exits app
      end
      
      require 'yaml'
      yml = YAML.load_file @options.config
      yml.each_pair do |key, value|
        @options.send(key.downcase+'=',value)
      end
    end

    def output_help!
      output_version
      RDoc::usage #exits app
    end

    def output_usage!
      RDoc::usage 'Usage' #exits app
    end

    def output_version
      puts "#{File.basename(__FILE__)} version #{VERSION}"
    end

    def process_command
      cmd = @arguments.pop
      
      unless %w[commit update create].include? cmd
        puts "Unknown command #{cmd}"
        RDoc::usage 'Examples' #exits app
      end
      
      #execute
      s5 = SimpleS3Sync.new(@options)
#      s5.send cmd
    end
end

app = SimpleS3SyncConfig.new(ARGV)
app.run














#--------CODE
#Installation/Options etc
class SimpleS3Sync
  #--------TASKS
  #Create bucket
  def create
    require 'S3'
    o = SimpleS3Sync.options
    
    conn = S3::AWSAuthConnection.new(o['AWS_ACCESS_KEY'], o['AWS_SECRET_ACCESS_KEY'], false)
    conn.create_bucket(o['BUCKET_NAME'])
    puts "BUCKET #{o['BUCKET_NAME']} "
  end
  #Upload changes to s3
  def commit
    install_required
    check_options_files
    o = SimpleS3Sync.options
    
    puts 'STARTING SYNC'
    cmds = build_cmds("ruby lib/s3sync/s3sync.rb -r --ssl --delete _SOURCE_ #{o['BUCKET_NAME']}:_DESTINATION_")
    `
    #{export_cmd}
    #{cmds}
    `
  end

  #Download from S3
  def update
    install_required
    check_options_files
    o = options
      
    puts 'STARTING UPDATE'
    cmds = build_cmds("ruby lib/s3sync/s3sync.rb -r --ssl --delete #{o['BUCKET_NAME']}:_DESTINATION_ _SOURCE_")
    `
    #{export_cmd}  
    #{cmds}
    `
  end
  
  #--------HELPERS
  private

  #download and install all required libaries/certificates
  def install_required
    unless File.exists? 'lib/s3sync/s3sync.rb'
      puts 'DOWNLOADING s3ync'
      `wget http://s3.amazonaws.com/ServEdge_pub/s3sync/s3sync.tar.gz`
      `tar xvzf s3sync.tar.gz`
      `mv s3sync lib/`
      `rm s3sync.tar.gz`
    end
    
    unless File.exists? 'lib/s3sync/s3sync_certificate'
      puts 'DOWNLOADING Certificate'
      `wget http://googlecode.com/svn/trunk/s3sync_certificate`
      `mv s3sync_certificate lib/s3sync/`
    end
  end
    
  #variables that need to be exported for s3sync
  def export_cmd
    o = options
    "
    export AWS_ACCESS_KEY_ID=#{o['AWS_ACCESS_KEY']};
    export AWS_SECRET_ACCESS_KEY=#{o['AWS_SECRET_ACCESS_KEY']};
    export SSL_CERT_FILE=lib/s3sync/s3sync_certificate;
    "
  end
  
  #checks if files are entered
  def check_options_files
    if(!OPTIONS['FILES'] or OPTIONS['FILES'].length==0) then
      puts 'NOTHING TO DO, specify files in s3sync.rake or s3sync.yml'
      exit
    end
  end
  
  #fill & multiply commands 
  # -replace source/destination placeholders
  def build_cmds
    o = options
    out = []
    
    o['FILES'].each_pair do |source, destination|
      out.push(cmd.sub('_SOURCE_',source.to_s).sub('_DESTINATION_',destination.to_s))#to_s in case of nil
    end
    out.join ";\n"
  end
end #impleS3Sync

#s5 = SimpleS3Sync.new
#case ARGV[0]
#  when "update"
#    s5.update
#  when "commit"
#    s5.commit
#  when "create"
#    s5.create
#  else
#    puts "commit OR update OR create"
#    exit
#end  












